/*
**  File: AquaFiles.c
**
**  This file is part of the AQUA program suite, which is being
**  developed in the NMR Spectroscopy Department, Bijvoet Center for 
**  Biomolecular Research, Utrecht. 
**
**
**  AUTHOR(S) : Ton Rullmann
**  VERSION   : 1.5.0
**  DATE      : Dec 18, 1997
**
**  This file contains miscellaneous functions dealing with Aqua data files.
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "Compiler.h"
#include "General.h"

#include "AquaMacros.h"
#include "AquaTypes.h"
/* #include "AquaData.h" */

/****************  function prototypes  ****************/

#include "AquaFuncts.h"
#include "AquaStrucset.h"
#include "MenuFuncts.h"
#include "Functs.h"

#include "AquaFiles.h"

/****************  globals  ****************/

extern Boolean  iaf;                         /* import from Functs */
extern Card     msg_string;                  /* import from Functs */
extern int      terseness;                   /* import from Functs */

FILE     *fpiG, *fprG, *fpvG;
Filnam   dinf_filnamG, drst_filnamG, dval_filnamG;
Filnam   dinf_filnamNormal;       /* quick fix: normal file corresponding to 
                                     extended file read in ProcessDistinfFile */

static Card     line, descr, inp;


/* BEGIN_EXTH */

/****************  NewDistdataListElem  ****************/
/*
**  Allocate a new data list element, and set the fst, lst and next pointers
**  and up the count.
*/

DistdataDesc *NewDistdataListElem( DistdataList *list, DistdataDesc *rel_desc )
{
    char          *fct_name = "NewDistdataListElem";
    DistdataDesc  *new_desc;

    new_desc = CREATE_NEW( DistdataDesc, 1 );
    if ( !new_desc )
	NOMEM( "new_desc" );
    if ( list->count == 0 )
	list->fst_desc = new_desc;	    
    else
	list->lst_desc->next = new_desc;	    
    list->count++;
    list->lst_desc = new_desc;
    
    if ( rel_desc )
    {
	new_desc->related = rel_desc;
	rel_desc->related = new_desc;
    }
    
    return( new_desc );
}

/****************  DestroyDistdataListElem  ****************/
/*
**  Free the memory belonging to the actual data.
**  For now the element itself, and therefore the list count as well, 
**  remain unchanged.
*/

void DestroyDistdataListElem( DistdataList *list, void *data, Boolean destroy_related )
{
    int            count = list->count;
    DistdataDesc   *elem = list->fst_desc;

    while ( count-- )
    {
	if ( elem->data == data )
	{
	    free( data );
	    elem->data = NULL;
	    elem->type = dd_unktyp;
	    if ( elem->related )
		elem->related->related = NULL;
	    if ( destroy_related && elem->related )
	    {
		free( elem->related->data );
		elem->related->type = dd_unktyp;
		elem->related = NULL;
	    }
	}
	elem = elem->next;
    }
}

/****************  PrintDistdataList  ****************/
/*
**  Print info about the DistdataList.
*/

void PrintDistdataList( DistdataList *list )
{
    int            count = list->count;
    DistdataDesc   *elem = list->fst_desc;

    while ( count-- )
    {
	printf( "%i %p", elem->type, elem );
	if ( elem->related )
	    printf( " %p\n", elem->related );
	else
	    putchar( '\n' );
	elem = elem->next;
    }
    printf( "  %p\n", list->lst_desc );    
}

/****************  ProcessDistinfFile  ****************/
/*
**  Read a distance info file (cf. WriteAquaDRFiles in AquaHow).
**  If <dinf_filnam> is an empty string the user is prompted to supply a 
**  file name, otherwise the string value is used.
**  If <*ftype_inf> is inf_unktyp, the value read from the file will be returned
**  in it, otherwise it should match the file type (inf_normal, inf_extended).
**  The caller should always check whether the returned value is OK.
**  If <save_hdr> the header lines are stored in the text buffer.
**  The other arguments return data to the calling routine.
**
**  Internally, the inf_normal type covers both single model and multiple
**  model files (cf. WriteDistinfFile). If, however, more than one model
**  is encountered the value inf_multi_model is returned via <*ftype_inf>.
*/

void ProcessDistinfFile( DistdataList *inf_list, int *dist_count, int *struc_count, 
                Qtyp *qtype_inf, Filnam set_filnam, Filnam drst_filnam, Card ident, 
                const Boolean save_hdr, DistinfTyp *ftype_inf, Filnam dinf_filnam ) 
{
    char          *fct_name = "ProcessDistinfFile";
    DistinfTyp    ftype = *ftype_inf;
    int           s_count = 0, d_count = 0, a_count = 0, totreadcnt = 0;
    int           readcnt, s_cnt, cnt, struc_num;
    DistdataDesc  *new_desc, *lst_desc;
    Distinf       *dist,  *dist_inf;
    Distinfl      *distl, *dist_infl;
    Distidx       *idxall;
    Menu          *oldmenu;
    Boolean       hdrtypnw = false, done = false;
    Card          type, ident2;
    FILE          *fpi;
    Qtyp          qt, qtype = undef_data;
    Filnam        filnam_s, filnam_r, filnam_n;

    static MenuItem menulist1[] =
    {
        { inf_normal,       "normal",   none, on },
        { inf_extended,     "extended", none, on }
    };
    static MenuItem menulist2[] =
    {
        { noe_inf_data, "noe", none, on },
        { ss_inf_data,  "ss",  none, on },
        { hb_inf_data,  "hb",  none, on },
        { dis_inf_data, "dis", none, on } 
    };
    static Menu menu1 =
    {
        "Distinf menu 1", "distance-info-file type", "Give type:", 
        sizeof(menulist1)/sizeof(MenuItem)
    };
    static Menu menu2 =
    {
        "Distinf menu 2", "distance-info-file selector", "Give selector:", 
        sizeof(menulist2)/sizeof(MenuItem)
    };

    fpi = NULL;
    dist_inf = NULL;
    dist_infl = NULL;
    *type = '\0';
    *set_filnam = '\0';
    *drst_filnam = '\0';
    *dinf_filnamNormal = '\0';
   
    oldmenu = SetMenu( &menu1, menulist1 );

/* if a file name is given, use it, otherwise ask the user */

    if ( !IsEmptyStr( dinf_filnam ) )
    {
	if ( terseness < 6 ) {
            printf( "Now opening: %s\n", dinf_filnam );
        }
        if ( NOTOPENr( fpi, dinf_filnam ) )
        {
            printf( " * Distance-info file <%s> could not be opened\n", dinf_filnam );
            exit( 1 );
        }
    }
    else
    {
        while ( true )
        {
	    if ( ftype == inf_unktyp )         /* initial value not set by call */
		ftype = (DistinfTyp) ReadMenuItem();
	    switch ( ftype )
	    {
		case inf_normal:
		case inf_extended:
		    goto next;
		case item_NULL:
                    goto end;
                default:
                    ProcessMenuError( menu_fatal );
                    break;
	    }
	    ftype = inf_unktyp;
	}
next:
	SetMenu( &menu2, menulist2 );
	while ( true )
	{
            qtype = (Qtyp) ReadMenuItem();
            switch ( qtype )
            {
                case noe_inf_data:
		    if ( ftype == inf_normal )
			qt = noe_inf_data;
		    else
			qt = noe_infl_data;
		    goto data;
                case ss_inf_data:
		    if ( ftype == inf_normal )
			qt = ss_inf_data;
		    else
			qt = ss_infl_data;
		    goto data;
                case hb_inf_data:
		    if ( ftype == inf_normal )
			qt = hb_inf_data;
		    else
			qt = hb_infl_data;
		    goto data;
                case dis_inf_data:
		    if ( ftype == inf_normal )
			qt = dis_inf_data;
		    else
			qt = dis_infl_data;
		    goto data;
                case item_NULL:
                    goto end;
                default:
                    ProcessMenuError( menu_fatal );
                    break;
            }
	}
data:
	qtype = qt;
    
	fpi = OpenAquaFile( qtype, "r", dinf_filnam, false );
	if ( !fpi )
	    goto end;
    }

/* start file processing */

    if ( !ReadAquaIDLines( fpi ) )
	goto err;


/* process distance-info-file header and if save_hdr copy to the text buffer */

    while ( !done )
    {
	if ( ! ProcessAquaLine( line, descr, inp, size_LINE, fpi ) )
	    goto err;
        if      ( EQn( descr, "IDENT", 5 ) )
            strncpy( ident, inp, size_LINE-1 );
        else if ( EQn( descr, "TYPE", 4 ) )
            strcpy( type, inp );
        else if ( EQn( descr, "DIST_COUNT", 10 ) )
            sscanf( inp, "%i", &d_count );
        else if ( EQn( descr, "ATOM_COUNT", 10 ) )
            sscanf( inp, "%i", &a_count );
        else if ( EQn( descr, "Molecule data", 13 ) )
            strcpy( set_filnam, inp );
        else if ( EQn( descr, "Restraint data", 14 ) )
            strcpy( drst_filnam, inp );
        else if ( EQn( descr, "Normal info data", 16 ) )
            strcpy( dinf_filnamNormal, inp );
        else if ( EQn( descr, "STRUC_COUNT", 11 ) )
	{
	    hdrtypnw = true;
            sscanf( inp, "%i", &s_count );
	    continue;                               /* don't store in text buffer */
	}
	else if ( EQn( descr, "STRUCTURE", 9 ) )
	{
	    sscanf( inp, "%i", &struc_num ) ;
            done = true;                            /* new style header */
	}
        else if ( EQn( descr, "BEGIN_DATA", 10 ) )
            done = true;                            /* old style header */

        if ( save_hdr && !done )
	    SaveToTxtBuffer( line );
    }

/* check presence of data */

    if ( !s_count )
	s_count = 1;
    if ( !d_count )
    {
        puts( " * Number of distances not specified in distance-info-file header" );
        goto err;
    }
    if ( IsEmptyStr( set_filnam ) )
    {
        puts( " * No file name for strucset specified in distance-info-file header" );
        goto err;
    }
    if ( IsEmptyStr( type ) )
    {
        puts( " * No type specified in distance-info-file header" );
        goto err;
    }
    else if ( EQn( type, "normal", 6 ) )
    {
	if ( ftype != inf_normal && ftype != inf_unktyp )
	{
	    printf( " * Conflicting type <%s> specified in distance-info-file header\n", type );
	    goto err;
	}
	ftype = inf_normal;
    }
    else if ( EQn( type, "extended", 8 ) )
    {
	if ( ftype != inf_extended && ftype != inf_unktyp )
	{
	    printf( " * Conflicting type <%s> specified in distance-info-file header\n", type );
	    goto err;
	}
	ftype = inf_extended;
    }
    else
    {
        printf( " * Unknown type <%s> specified in distance-info-file header\n", type );
        goto err;
    }
    if ( ftype == inf_extended && !a_count )
    {
        puts( " * Number of atoms not specified in distance-info-file header" );
        goto err;
    }
    if ( ftype == inf_extended && s_count > 1 )
    {
        puts( " * <extended> info file incompatible with multi-model specification" );
        goto err;
    }

/* save return value */

    *dist_count = d_count;
    *struc_count = s_count;
    
/* read the indices etc. from the distance info file */

    if ( ftype == inf_normal )

/* normal distinf file type */

    {
	dist = CREATE_NEW( Distinf, d_count * s_count );
	if ( !dist )
	    NOMEM( "dist" );
	
	dist_inf = dist;

	while ( s_count-- )
	{
	    done = !hdrtypnw;
	    d_count = *dist_count;
	    while ( !done )
	    {
		if ( ! ProcessAquaLine( line, descr, inp, size_LINE, fpi ) )
		    goto err;
		if ( EQn( descr, "STRUCTURE", 9 ) )
		    sscanf( inp, "%i", &struc_num );
		else if ( EQn( descr, "BEGIN_DATA", 10 ) )
		    done = true;
	    }
	    while ( d_count-- )
	    {
			    /* note: incompleteness of a line will not be noticed, 
			    fscanf reads through \n */
    
    	    if ( fscanf( fpi, "%i %i %i %i %i", &dist->dtype, &dist->idx_A, 
			     &dist->idx_B, &dist->res_A, &dist->res_B ) < 5 )
		{
		    ERROR( " * Read error or end of file" );
		    goto err;
		}
		dist++;
		FlushLine( fpi );
	    }
	}
    }
    else

/* extended distinf file type */

    {
	distl = CREATE_NEW( Distinfl, d_count );
	if ( !distl )
	    NOMEM( "distl" );
	idxall = CREATE_NEW( Distidx, a_count );
	if ( !idxall )
	    NOMEM( "idxall" );
	
	dist_infl = distl;
	while ( d_count-- )
	{
                        /* note: incompleteness of a line will not be noticed, 
			fscanf reads through \n */

	    if ( fscanf( fpi, "%i %i %i %i %i %i %i", &distl->dtype, 
	                 &distl->atype_A, &distl->atype_B, &distl->count_A, 
	                 &distl->count_B, &distl->ass_A, &distl->ass_B ) < 7 )
	    {
		ERROR( " * Read error or end of file" );
		goto err;
	    }

	    readcnt = distl->count_A + distl->count_B;
	    if ( ( totreadcnt+=readcnt ) > a_count )
	    {
		    ERROR( " * Out of idxall array - code error" );
		    goto err;
	    }

	    distl->A = idxall;
	    distl->B = idxall + distl->count_A;
	    cnt = readcnt;
	    while ( cnt-- )
	    {
		if ( fscanf( fpi, "%i", &idxall->idx ) < 1 )
		{
		    ERROR( " * Read error or end of file" );
		    goto err;
		}
		idxall++;
	    }
	    idxall = distl->A;
	    cnt = readcnt;
	    while ( cnt-- )
	    {
		if ( fscanf( fpi, "%i", &idxall->res ) < 1 )
		{
		    ERROR( " * Read error or end of file" );
		    goto err;
		}
		idxall++;
	    }
	    distl++;
	    FlushLine( fpi );
	}
	if ( totreadcnt != a_count )
	{
	    ERROR( " * Incorrect size of idxall array - code error" );
	    goto err;
	}
    }

end:
    ResetMenu( oldmenu );
    if ( fpi )
        fclose( fpi );

/* save return values, and add to the distinf data list */

    *ftype_inf = *struc_count > 1 ? inf_multi_model : ftype;
    *qtype_inf = qtype;
    new_desc = NewDistdataListElem( inf_list, NULL );
    if ( ftype == inf_normal )
    {
	new_desc->data = (void *)dist_inf;
	new_desc->type = dd_inf_normal;
    }
    else
    {
	new_desc->data = (void *)dist_infl;
	new_desc->type = dd_inf_extended;
        if ( IsEmptyStr( dinf_filnamNormal ) ) 
	    return;

/* special case: store normal distinf belonging to extended distinf just read */

	lst_desc = inf_list->lst_desc;
	strcpy( filnam_n, dinf_filnamNormal );
	ftype = inf_normal;
	ProcessDistinfFile( inf_list, &cnt, &s_cnt, &qt, filnam_s, filnam_r, 
                            ident2, false, &ftype, filnam_n );
	if ( NOTEQ( set_filnam, filnam_s )   ||
	     *dist_count != cnt || *struc_count != s_cnt )
	    FATAL( "Inconsistent information on normal and extended info files" );
	lst_desc->related = inf_list->lst_desc;
	inf_list->lst_desc->related = lst_desc;
    }

    return;

err:
    exit( 1 );
}

/****************  ProcessDistvalFile  ****************/
/*
**  Read a distance values file (cf. AquaDist).
**  If <dval_filnam> is an empty string the user is prompted to supply a 
**  file name, otherwise the string value is used.
**  If <*ftype_val> is inf_unktyp, the value read from the file will be returned
**  in it, otherwise it should match the file type (inf_normal, inf_extended).
**  The caller should always check whether the returned value is OK.
**  If <save_hdr> the header lines are stored in the text buffer.
**  The other arguments return data to the calling routine.
**  A Model array is allocated if necessary (but the size is not checked if
**  it already exists), and the <struc_num> fields are set.
*/

void ProcessDistvalFile( DistdataList *val_list, int *dist_count, int *struc_count, 
                Model **models, Qtyp *qtype_val, Filnam set_filnam, 
		Filnam dinf_filnam,  Filnam drst_filnam, Card ident, 
		const Boolean save_hdr, DistinfTyp *ftype_val, Filnam dval_filnam )
{
    char          *fct_name = "ProcessDistvalFile";
    DistinfTyp    ftype = *ftype_val;
    int           d_count = 0, s_count = 0;
    int           dt_count = 0, totreadcnt = 0, readcnt;
    DistdataDesc  *new_desc;
/*    DistdataDesc  *lst_desc; */
    Distval       *dist, *dist_val;
    Distvall      *distl, *dist_vall;
    Distval       *valuesall;
    Model         *mdl = NULL;
    Menu          *oldmenu;
    Boolean       done = false;
    Card          type;
    FILE          *fpv;
    Qtyp          qt, qtype = undef_data;

    static MenuItem menulist1[] =
    {
        { inf_normal,   "normal",   none, on },
        { inf_extended, "extended", none, on }
    };
    static MenuItem menulist2[] =
    {
        { noe_val_data, "noe", none, on },
        { ss_val_data,  "ss",  none, on },
        { hb_val_data,  "hb",  none, on },
        { dis_val_data, "dis", none, on } 
    };
    static Menu menu1 =
    {
        "Distinf menu 1", "distance-values-file type", "Give type:", 
        sizeof(menulist1)/sizeof(MenuItem)
    };
    static Menu menu2 =
    {
        "Distval menu2", "distance-values-file selector", "Give selector:", 
        sizeof(menulist2)/sizeof(MenuItem)
    };

    fpv = NULL;
    dist_val = NULL;
    dist_vall = NULL;
    *set_filnam = '\0';
    *dinf_filnam = '\0';
    *drst_filnam = '\0';
    
    oldmenu = SetMenu( &menu1, menulist1 );

/* if a file name is given, use it, otherwise ask the user */

    if ( !IsEmptyStr( dval_filnam ) )
    {
	if ( terseness < 6 ) {
	    printf( "Now opening: %s\n", dval_filnam );
        }
        if ( NOTOPENr( fpv, dval_filnam ) )
        {
            printf( " * Distance-values file <%s> could not be opened\n", dval_filnam );
            exit( 1 );
        }
    }
    else
    {
        while ( true )
        {
	    if ( ftype == inf_unktyp )         /* initial value not set by call */
		ftype = (DistinfTyp) ReadMenuItem();
	    switch ( ftype )
	    {
		case inf_normal:
		case inf_extended:
		    goto next;
		case item_NULL:
                    goto end;
                default:
                    ProcessMenuError( menu_fatal );
                    break;
	    }
	    ftype = inf_unktyp;
	}
next:
	SetMenu( &menu2, menulist2 );
	while ( true )
        {
            qtype = (Qtyp) ReadMenuItem();
            switch ( qtype )
            {
                case noe_val_data:
		    if ( ftype == inf_normal )
			qt = noe_val_data;
		    else
			qt = noe_vall_data;
		    goto data;
                case ss_val_data:
		    if ( ftype == inf_normal )
			qt = ss_val_data;
		    else
			qt = ss_vall_data;
		    goto data;
                case hb_val_data:
		    if ( ftype == inf_normal )
			qt = hb_val_data;
		    else
			qt = hb_vall_data;
		    goto data;
                case dis_val_data:
		    if ( ftype == inf_normal )
			qt = dis_val_data;
		    else
			qt = dis_vall_data;
		    goto data;
                case item_NULL:
                    goto end;
                default:
                    ProcessMenuError( menu_fatal );
                    break;
            }
        }
data:
	qtype = qt;
    
	fpv = OpenAquaFile( qtype, "r", dval_filnam, false );
	if ( !fpv )
	    goto end;
    }

/* start file processing */

    if ( !ReadAquaIDLines( fpv ) )
	goto err;

/* process distance-values-file header and if save_hdr copy to the text buffer */

    while ( !done )
    {
	if ( ! ProcessAquaLine( line, descr, inp, size_LINE, fpv ) )
	    goto err;
        if      ( EQn( descr, "IDENT", 5 ) )
            strncpy( ident, inp, size_LINE-1 );
        else if ( EQn( descr, "TYPE", 4 ) )
            strcpy( type, inp );
        else if ( EQn( descr, "DIST_COUNT", 10 ) )
            sscanf( inp, "%i", &d_count );
        else if ( EQn( descr, "TOTAL_DIST_COUNT", 16 ) )
            sscanf( inp, "%i", &dt_count );
        else if ( EQn( descr, "Molecule data", 13 ) )
            strcpy( set_filnam, inp );
        else if ( EQn( descr, "Restraint data", 14 ) )
            strcpy( drst_filnam, inp );
        else if ( EQn( descr, "Info data", 9 ) )
            strcpy( dinf_filnam, inp );
        else if ( EQn( descr, "STRUC_COUNT", 11 ) )
	{
            sscanf( inp, "%i", &s_count );
	    if ( !(*models) )
	    {
		mdl = CREATE_NEW( Model, s_count );
		if ( !mdl )
		    NOMEM( "models" );
		*models = mdl;
	    }
	    else
		mdl = *models;
	}
        else if ( EQn( descr, "STRUCTURE", 9 ) )
	{
            sscanf( inp, "%i", &(mdl++->struc_num) );
            done = true;
	}

        if ( save_hdr && !done )
	    SaveToTxtBuffer( line );
    }

/* check presence of data */

    if ( !d_count )
    {
        puts( " * Number of distances not specified in distance-values-file header" );
        goto err;
    }
    if ( !s_count )
    {
        puts( " * Number of structures not specified in distance-values-file header" );
        goto err;
    }
    if ( IsEmptyStr( set_filnam ) )
    {
        puts( " * No file name for strucset specified in distance-values-file header" );
        goto err;
    }
    if ( IsEmptyStr( dinf_filnam ) )
    {
        puts( " * No file name for distance-info file specified in distance-values-file header" );
        goto err;
    }
    if ( IsEmptyStr( type ) )
    {
        puts( " * No type specified in distance-values-file header" );
        goto err;
    }
    else if ( EQn( type, "normal", 6 ) )
    {
	if ( ftype != inf_normal && ftype != inf_unktyp )
	{
	    printf( " * Conflicting type <%s> specified in distance-values-file header\n", type );
	    goto err;
	}
	ftype = inf_normal;
    }
    else if ( EQn( type, "extended", 8 ) )
    {
	if ( ftype != inf_extended && ftype != inf_unktyp )
	{
	    printf( " * Conflicting type <%s> specified in distance-values-file header\n", type );
	    goto err;
	}
	ftype = inf_extended;
    }
    else
    {
        printf( " * Unknown type <%s> specified in distance-values-file header\n", type );
        goto err;
    }
    if ( ftype == inf_extended && !dt_count )
    {
        puts( " * Total number of distances not specified in distance-values-file header" );
        goto err;
    }

/* save return values */

    *dist_count = d_count;
    *struc_count = s_count;

/* read the values from the distance values file */

    if ( ftype == inf_normal )

/* normal distval file type */

    {
	dist = CREATE_NEW( Distval, s_count * d_count );
	if ( !dist )
	    NOMEM( "dist" );
    
	dist_val = dist;
    
	while ( s_count-- )
	{
	    done = false;
	    d_count = *dist_count;
	    while ( !done )
	    {
		if ( ! ProcessAquaLine( line, descr, inp, size_LINE, fpv ) )
		    goto err;
		if ( EQn( descr, "STRUCTURE", 9 ) )
		    sscanf( inp, "%i", &(mdl++->struc_num) );
		else if ( EQn( descr, "BEGIN_DATA", 10 ) )
		    done = true;
	    }
	    while ( d_count-- )
	    {
			    /* note: incompleteness of a line will not be noticed, 
			    fscanf reads through \n */
    
		if ( fscanf( fpv, "%f", dist++ ) < 1 )
		{
		    ERROR( " * Read error or end of file" );
		    goto err;
		}
		FlushLine( fpv );
	    }
	}
    }
    else

/* extended distval file type */

    {
	distl = CREATE_NEW( Distvall, s_count * d_count );
	if ( !distl )
	    NOMEM( "distl" );
	valuesall = CREATE_NEW( Distval, s_count * dt_count );
	if ( !valuesall )
	    NOMEM( "valuesall" );
    
	dist_vall = distl;
    
	while ( s_count-- )
	{
	    done = false;
	    d_count = *dist_count;
	    while ( !done )
	    {
		if ( ! ProcessAquaLine( line, descr, inp, size_LINE, fpv ) )
		    goto err;
		if ( EQn( descr, "STRUCTURE", 9 ) )
		    sscanf( inp, "%i", &(mdl++->struc_num) );
		else if ( EQn( descr, "BEGIN_DATA", 10 ) )
		    done = true;
	    }
	    totreadcnt = 0;
	    while ( d_count-- )
	    {
			    /* note: incompleteness of a line will not be noticed, 
			    fscanf reads through \n */
    
		if ( fscanf( fpv, "%i", &distl->count ) < 1 )
		{
		    ERROR( " * Read error or end of file" );
		    goto err;
		}

		if ( ( totreadcnt+=distl->count ) > dt_count )
		{
			ERROR( " * Out of valuesall array - code error" );
			goto err;
		}
    
		distl->values = valuesall;
		readcnt = distl->count;
		while ( readcnt-- )
		{
		    if ( fscanf( fpv, "%f", valuesall ) < 1 )
		    {
			ERROR( " * Read error or end of file" );
			goto err;
		    }
		    valuesall++;
		}
		distl++;
		FlushLine( fpv );
	    }
	    if ( totreadcnt != dt_count )
	    {
		ERROR( " * Incorrect size of valuesall array - code error" );
		goto err;
	    }
	}
    }

end:
    ResetMenu( oldmenu );
    if ( fpv )
        fclose( fpv );

/* save return values, and add to the distval data list */

    *qtype_val = qtype;
    *ftype_val = ftype;
    new_desc = NewDistdataListElem( val_list, NULL );
    if ( ftype == inf_normal )
    {
	new_desc->data = (void *)dist_val;
	new_desc->type = dd_val_normal;
    }
    else
    {
	new_desc->data = (void *)dist_vall;
	new_desc->type = dd_val_extended;
    }
    return;

err:
    exit( 1 );
}

/****************  ProcessDistrstFile  ****************/
/*
**  Read a distance restraints file (cf. WriteAquaDRFiles in AquaHow)
**  and return a pointer to a Distrst list.
**  If <drst_filnam> is an empty string the user is prompted to supply a 
**  file name, otherwise the string value is used.
**  If <save_hdr> the header lines are stored in the text buffer.
**  The other arguments return data to the calling routine.
*/

Distrst *ProcessDistrstFile( int *dist_count, Qtyp *qtype_rst, Filnam set_filnam,
                   Filnam dinf_filnam, Card ident, 
		   const Boolean save_hdr, Filnam drst_filnam )
{
    char      *fct_name = "ProcessDistrstFile";
    int       d_count = 0;
    Distrst   *dist, *dist_rst;
    Menu      *oldmenu;
    Boolean   done = false;
    FILE      *fpc;
    Qtyp      qtype = undef_data;

    static MenuItem menulist[] =
    {
        { noe_rst_data, "noe", none, on },
        { ss_rst_data,  "ss",  none, on },
        { hb_rst_data,  "hb",  none, on },
        { dis_rst_data, "dis", none, on }
    };
    static Menu menu =
    {
        "Distrst menu", "distance-restraints-file selector", "Give selector:", 
        sizeof(menulist)/sizeof(MenuItem)
    };

    fpc = NULL;
    dist_rst = NULL;
    *set_filnam = '\0';
    *dinf_filnam = '\0';
    
    oldmenu = SetMenu( &menu, menulist );

/* if a file name is given, use it, otherwise ask the user */

    if ( !IsEmptyStr( drst_filnam ) )
    {
	if ( terseness < 6 ) {
	    printf( "Now opening: %s\n", drst_filnam );
        }
        if ( NOTOPENr( fpc, drst_filnam ) )
        {
            printf( " * Distance-restraints file <%s> could not be opened\n", drst_filnam );
            exit( 1 );
        }
    }
    else
    {
        while ( true )
        {
            qtype = (Qtyp) ReadMenuItem();
            switch ( qtype )
            {
                case noe_rst_data:
                case ss_rst_data:
                case hb_rst_data:
                case dis_rst_data:
                   fpc = OpenAquaFile( qtype, "r", drst_filnam, false );
                    if ( fpc )
                        goto data;
                    else
                        break;
                case item_NULL:
                    goto end;
                default:
                    ProcessMenuError( menu_fatal );
                    break;
            }
        }
    }

data:
    if ( !ReadAquaIDLines( fpc ) )
	goto err;

/* process distance-restraints-file header and if save_hdr copy to the text buffer */

    while ( !done )
    {
	if ( ! ProcessAquaLine( line, descr, inp, size_LINE, fpc ) )
	    goto err;
        if      ( EQn( descr, "IDENT", 5 ) )
            strncpy( ident, inp, size_LINE-1 );
        else if ( EQn( descr, "DIST_COUNT", 10 ) )
            sscanf( inp, "%i", &d_count );
        else if ( EQn( descr, "Molecule data", 13 ) )
            strcpy( set_filnam, inp );
        else if ( EQn( descr, "Info data", 9 ) )
            strcpy( dinf_filnam, inp );
        else if ( EQn( descr, "BEGIN_DATA", 10 ) )
            done = true;

        if ( save_hdr && !done )
	    SaveToTxtBuffer( line );
    }

/* check presence of data */

    if ( !d_count )
    {
        puts( " * Number of distances not specified in distance-restraints-file header" );
        goto err;
    }
    if ( IsEmptyStr( set_filnam ) )
    {
        puts( " * No file name for strucset specified in distance-restraints-file header" );
        goto err;
    }
    if ( IsEmptyStr( dinf_filnam ) )
    {
        puts( " * No file name for distance-info file specified in distance-restraints-file header" );
        goto err;
    }

/* read the values from the distance-restraints file */

    dist = CREATE_NEW( Distrst, d_count );
    if ( !dist )
	NOMEM( "dist" );

    dist_rst = dist;
    *dist_count = d_count;

    while ( d_count-- )
    {
	if ( fscanf( fpc, "%c %f %f %f", &dist->typ_bnd, 
	                  &dist->lo_bnd, &dist->up_bnd, &dist->dist ) < 4 )
	{
	    ERROR( "Read error or end of file" );
	    goto err;
	}
	dist++;
	FlushLine( fpc );
    }

end:
    ResetMenu( oldmenu );
    if ( fpc )
        fclose( fpc );
    *qtype_rst = qtype;
    return ( dist_rst );

err:
    exit( 1 );
    return ( NULL ); /* Never to be executed but included for stupidity */
}

/****************  CreateAquaDistFiles  ****************/
/*
**  Open a set of new Distinf, Distrst and Distval files.
**  The file names and pointers are stored in variables global to this file.
*/

void CreateAquaDistFiles( char *DR_type, DistinfTyp ftyp, char *msg, 
                          Boolean do_rst, Boolean do_val )
{
    Qtyp    qti, qtr, qtv;
    

/* open the files */

    if ( EQ( DR_type, "noe" ) )
    {
	qti = noe_inf_data;
	qtr = noe_rst_data;
	qtv = noe_val_data;
	if ( ftyp == inf_extended )
	{
	    qti = noe_infl_data;
	    qtv = noe_vall_data;
	}
    }
    else if ( EQ( DR_type, "ss" ) )
    {
	qti = ss_inf_data;
	qtr = ss_rst_data;
	qtv = ss_val_data;
	if ( ftyp == inf_extended )
	{
	    qti = ss_infl_data;
	    qtv = ss_vall_data;
	}
    }
    else if ( EQ( DR_type, "hb" ) )
    {
	qti = hb_inf_data;
	qtr = hb_rst_data;
	qtv = hb_val_data;
	if ( ftyp == inf_extended )
	{
	    qti = hb_infl_data;
	    qtv = hb_vall_data;
	}
    }
    else if ( EQ( DR_type, "dis" ) )
    {
	qti = dis_inf_data;
	qtr = dis_rst_data;
	qtv = dis_val_data;
	if ( ftyp == inf_extended )
	{
	    qti = dis_infl_data;
	    qtv = dis_vall_data;
	}
    }
    else
    {
	printf( " * Illegal restraint type <%s>\n", DR_type );
	exit( 1 );
    }

    if ( terseness < 6 ) {
        printf( "%s - ", msg );
        if ( !iaf )
	    putchar( '\n' );
    }
    

    fpiG = OpenAquaFile( qti, "w", dinf_filnamG, false ); 
    if ( !fpiG )
    {
	if ( !iaf )
	    exit( 1 );
	return;
    }
    WriteAquaIDLines( fpiG, qti, dinf_filnamG );

    if ( do_rst )
    {
	fprG = OpenAquaFile( qtr, "w", drst_filnamG, true );
	if ( !fprG )
	{
	    if ( !iaf )
		exit( 1 );
	    return;
	}
	WriteAquaIDLines( fprG, qtr, drst_filnamG );
    }

    if ( do_val )
    {
	fpvG = OpenAquaFile( qtv, "w", dval_filnamG, true );
	if ( !fpvG )
	{
	    if ( !iaf )
		exit( 1 );
	    return;
	}
	WriteAquaIDLines( fpvG, qtr, dval_filnamG );
    }
}

/****************  WriteDistinfFile  ****************/
/*
**  For now, assume that the file has been created by CreateAquaDistFiles,
**  and that the names of the related files are available.
**  Future option: create file here. 
**
**  The routine can write multiple (restraint) models, as created in
**  MakeDistinf; the models are numbered sequentially (no <models> array 
**  as used in WriteDistval).
*/

void WriteDistinfFile( Distinf *dist_inf, const int count, const int struc_count, 
                       Card ident, Filnam set_filnam )
{
    Distinf     *inf;
    int         s_count, num;
    
/* write the data */

    fprintf( fpiG, "$ IDENT: %s\n", ident );
    fprintf( fpiG, "$ TYPE: normal\n" );
    fprintf( fpiG, "$ DIST_COUNT: %i\n", count);
    fprintf( fpiG, "$ Molecule data: %s\n", set_filnam );
    fprintf( fpiG, "$ Restraint data: %s\n", drst_filnamG );
    fprintf( fpiG, "$ STRUC_COUNT: %i\n", struc_count );
    inf = dist_inf;
    s_count = 0;
    while ( ++s_count <= struc_count )
    {
        fprintf( fpiG, "$ STRUCTURE: %i\n", s_count );
        fprintf( fpiG, "$ BEGIN_DATA: \n" );
	num = count;
	while ( num-- )
	{
	    fprintf( fpiG, "%i %i %i %i %i\n", inf->dtype, inf->idx_A, inf->idx_B,
		     inf->res_A, inf->res_B  );
	    inf++;
	}
    }
    fclose( fpiG );
}

/****************  WriteDistinflFile  ****************/
/*
**  For now, assume that the file has been created by CreateAquaDistinflFile,
**  after a previous call to CreateAquaDistFiles, 
**  and that the names of the related files are available.
**  Future option: create file here. 
**  Quick fix: assume that dinf_filnamNormal has been set, and write it to file.
*/

void WriteDistinflFile( Distinfl *dist_inf, const int count, const int a_count, 
                        Card ident, Filnam set_filnam )
{
    Distinfl    *inf;
    Distidx     *idx;
    int         num, i;
    
/* write the data */

    fprintf( fpiG, "$ IDENT: %s\n", ident );
    fprintf( fpiG, "$ TYPE: extended\n" );
    fprintf( fpiG, "$ DIST_COUNT: %i\n", count );
    fprintf( fpiG, "$ ATOM_COUNT: %i\n", a_count );
    fprintf( fpiG, "$ Molecule data: %s\n", set_filnam );
    fprintf( fpiG, "$ Restraint data: %s\n", drst_filnamG );
    fprintf( fpiG, "$ Normal info data: %s\n", dinf_filnamNormal );
    fprintf( fpiG, "$ BEGIN_DATA: \n" );
    num = count;
    inf = dist_inf;
    while ( num-- )
    {
        fprintf( fpiG, "%i %i %i %i %i %i %i", inf->dtype, inf->atype_A, inf->atype_B, 
	         inf->count_A, inf->count_B, inf->ass_A, inf->ass_B  );
	idx = inf->A;
	i = inf->count_A;
	while ( i-- )
	    fprintf( fpiG, " %i", (idx++)->idx );
	idx = inf->B;
	i = inf->count_B;
	while ( i-- )
	    fprintf( fpiG, " %i", (idx++)->idx );
	idx = inf->A;
	i = inf->count_A;
	while ( i-- )
	    fprintf( fpiG, " %i", (idx++)->res );
	idx = inf->B;
	i = inf->count_B;
	while ( i-- )
	    fprintf( fpiG, " %i", (idx++)->res );
	fputc( '\n', fpiG );
        inf++;
    }
    fclose( fpiG );
}

/****************  WriteDistrstFile  ****************/
/*
**  For now, assume that the file has been created by CreateAquaDistFiles,
**  and that the names of the related files are available.
**  Future option: create file here. 
*/

void WriteDistrstFile( Distrst *dist_rst, const int count, Card ident, Filnam set_filnam )
{
    Distrst     *rst;
    int         num;
    
/* write the data */

    fprintf( fprG, "$ IDENT: %s\n", ident );
    fprintf( fprG, "$ DIST_COUNT: %i\n", count);
    fprintf( fprG, "$ Molecule data: %s\n", set_filnam );
    fprintf( fprG, "$ Info data: %s\n", dinf_filnamG );
    fprintf( fprG, "$ BEGIN_DATA: \n" );
    num = count;
    rst = dist_rst;
    while ( num-- )
    {
        fprintf( fprG, "%c %f %f %f\n", rst->typ_bnd, rst->lo_bnd, rst->up_bnd, rst->dist );
        rst++;
    }
    fclose( fprG );
}

/****************  WriteDistvalFile  ****************/
/*
**  For now, assume that the file has been created by CreateAquaDistFiles,
**  and that the names of the related files are available.
**  Future option: create file here. 
*/

void WriteDistvalFile( Distval *dist_val, const int dist_count, Card ident, Filnam set_filnam,
                       Model *models, const int struc_count )
{
    Distval     *val;
    int         num, s_count;
    
/* write the data */

    fprintf( fpvG, "$ IDENT: %s\n", ident );
    fprintf( fpvG, "$ TYPE: normal\n" );
    fprintf( fpvG, "$ DIST_COUNT: %i\n", dist_count);
    fprintf( fpvG, "$ Molecule data: %s\n", set_filnam );
    fprintf( fpvG, "$ Restraint data: %s\n", drst_filnamG );
    fprintf( fpvG, "$ Info data: %s\n", dinf_filnamG );
    fprintf( fpvG, "$ STRUC_COUNT: %i\n", struc_count );
    val = dist_val;
    s_count = struc_count;
    while ( s_count-- )
    {
        fprintf( fpvG, "$ STRUCTURE: %i\n", models++->struc_num );
        fprintf( fpvG, "$ BEGIN_DATA: \n" );
        num = dist_count;
        while ( num-- )
            fprintf( fpvG, "%f\n", *val++ );
    }
    fclose( fpvG );
}

/****************  WriteDistvallFile  ****************/
/*
**  For now, assume that the file has been created by CreateAquaDistFiles,
**  and that the names of the related files are available.
**  Future option: create file here. 
*/

void WriteDistvallFile( Distvall *dist_vall, const int dist_count, const int tot_count, 
                        Card ident, Filnam set_filnam,
                        Model *models, const int struc_count )
{
    Distvall    *d_vall;
    Distval     *values;
    int         num, s_count, d_count;
    
/* write the data */

    fprintf( fpvG, "$ IDENT: %s\n", ident );
    fprintf( fpvG, "$ TYPE: extended\n" );
    fprintf( fpvG, "$ DIST_COUNT: %i\n", dist_count);
    fprintf( fpvG, "$ Molecule data: %s\n", set_filnam );
    fprintf( fpvG, "$ Restraint data: %s\n", drst_filnamG );
    fprintf( fpvG, "$ Info data: %s\n", dinf_filnamG );
    fprintf( fpvG, "$ TOTAL_DIST_COUNT: %i\n", tot_count);
    fprintf( fpvG, "$ STRUC_COUNT: %i\n", struc_count );
    d_vall = dist_vall;
    s_count = struc_count;
    while ( s_count-- )
    {
        fprintf( fpvG, "$ STRUCTURE: %i\n", models++->struc_num );
        fprintf( fpvG, "$ BEGIN_DATA: " );
	d_count = dist_count;
	while ( d_count-- )
	{
	    num = d_vall->count;
	    values = d_vall->values;
	    fprintf( fpvG, "\n%i", num );
	    while ( num-- )
		fprintf( fpvG, " %f", *values++ );
	    d_vall++;
	}
	fputc( '\n', fpvG );
    }
    fclose( fpvG );
}

/****************  ReadRestraintFile  ****************/
/*
**  Read distance restraints from an Aqua restraint file. (cf. ReadNoes_biosym)
**
**  Format example (cf. output generated by qconvr script):
**
**  Title
**  count 3 type restrtyp
**  typUPP  [CHAIN chain] GLY 2 [INSERT ins] HN [CHAIN chain] ALA 3 HN [INSERT ins] 4.00
**  typLOW  [CHAIN chain] GLY 2 [INSERT ins] HN [CHAIN chain] ALA 3 HN [INSERT ins] 2.00
**  typUPLO [CHAIN chain] GLY 2 [INSERT ins] HN [CHAIN chain] ALA 3 HN [INSERT ins] 4.00 2.00
**  typULD  [CHAIN chain] GLY 2 [INSERT ins] HN [CHAIN chain] ALA 3 HN [INSERT ins] 4.00 2.00 3.00
**
**  where 
**    typ is: NOE, SS, HB 
**        (or possibly something else; max. (size_KEY - 5) characters )
**    restryp is: biosym, xplor, disgeo etc.
**    chain is chain (segment) identifier
**    ins is residue insertion code
**
**  Lines starting with # are treated as comments.
**  Empty or blank lines are skipped.
**  Lines beginning with an unrecognized keyword are printed and skipped.
**
**  The restraint atom names are stored in the external field.
**  The restraint residue numbers are shifted by <shift>.
**  The residue ID is completed ** by calling MakeUniqResid **.
*/

#define size_KEY 20

int ReadRestraintFile( FILE *fp, DRestraint *restrs, const int count, 
                       Card ident, char *typ, const int shift )
{
    char     *fct_name = "ReadRestraintFile";
    int      restr_read = 0;
    Atom     *at;
    Card     line;
    Boolean  done = false;
    char     key[size_KEY], utyp[size_KEY], ltyp[size_KEY], ultyp[size_KEY], 
             uldtyp[size_KEY];

    if ( OptionTrue( opt_prtrst ) )
	STATUS( "entering" );

/* set the keywords */

    strcpy( utyp, typ );
    ToUpperStr( utyp );
    sprintf( msg_string, "expecting restraints of type %s", utyp );
    if ( terseness < 6 ) {
        STATUSm;
    }
    strcpy( ltyp,   utyp );
    strcpy( ultyp,  utyp );
    strcpy( uldtyp, utyp );
    strcat( utyp,   "UPP" );
    strcat( ltyp,   "LOW" );
    strcat( ultyp,  "UPLO" );
    strcat( uldtyp, "ULD" );

    ReadLine( ident, size_LINE, fp );
    FlushLine( fp );

    while ( !done )
    {

/* read keyword */

	*key = '\0';
	if ( !ReadWord( key, size_KEY, true, fp ) )
	{
	    if ( strlen( key ) )
		printf( "skipping line beginning with: %s\n", key );
	    done = FlushLine( fp ) < 0;
	    continue;
	}
	if ( EQn( key, "#", 1 ) )
	{
	    done = FlushLine( fp ) < 0;
	    continue;
	}
	if ( ++restr_read > count )
	{
	    printf( " * More input than the expected %i restraints - cannot handle\n",
		    count );
	    exit( 1 );
	}

/* read first atom */

	at = &restrs->atom_A;
	if ( !ReadWord( line, size_LINE, true, fp ) )
	    goto err;
	if ( EQ( line, "CHAIN" ) )
	{
	    if ( !ReadWord( line, size_LINE, true, fp ) )
	        goto err;
	    CopyChars( at->chain, line, size_ID );
	    if ( !ReadWord( line, size_LINE, true, fp ) )
	        goto err;
	}
	CopyChars( at->res_nam, line, size_ID );
	if ( !ReadWord( line, size_LINE, true, fp ) )
	    goto err;
	if ( sscanf( line, "%i", &at->res_num_orig ) < 1 )
	    goto err;
	if ( !ReadWord( line, size_LINE, true, fp ) )
	    goto err;
	if ( EQ( line, "INSERT" ) )
	{
	    if ( !ReadWord( line, size_LINE, true, fp ) )
	        goto err;
	    at->res_insert = line[0];
	    if ( !ReadWord( line, size_LINE, true, fp ) )
	        goto err;
	}
	CopyChars( at->atom_ext, line, size_ID );
	at->res_num_orig += shift;
	MakeUniqResid( at );

/* read second atom */

	at = &restrs->atom_B;
	if ( !ReadWord( line, size_LINE, true, fp ) )
	    goto err;
	if ( EQ( line, "CHAIN" ) )
	{
	    if ( !ReadWord( line, size_LINE, true, fp ) )
	        goto err;
	    CopyChars( at->chain, line, size_ID );
	    if ( !ReadWord( line, size_LINE, true, fp ) )
	        goto err;
	}
	CopyChars( at->res_nam, line, size_ID );
	if ( !ReadWord( line, size_LINE, true, fp ) )
	    goto err;
	if ( sscanf( line, "%i", &at->res_num_orig ) < 1 )
	    goto err;
	if ( !ReadWord( line, size_LINE, true, fp ) )
	    goto err;
	if ( EQ( line, "INSERT" ) )
	{
	    if ( !ReadWord( line, size_LINE, true, fp ) )
	        goto err;
	    at->res_insert = line[0];
	    if ( !ReadWord( line, size_LINE, true, fp ) )
	        goto err;
	}
	CopyChars( at->atom_ext, line, size_ID );
	at->res_num_orig += shift;
	MakeUniqResid( at );

/* read bound(s) */

	if ( EQ( key, utyp ) ) 
	{
    	    if ( fscanf( fp, "%f", &restrs->up_bnd ) < 1 )
		goto err;
	    restrs->lo_bnd = 0.F;           /* set to default; but presence of */
	                                    /* bound is indicated by stat value */
	    restrs->stat = bnd_upper;
	}
	else if ( EQ( key, ltyp ) )
	{
    	    if ( fscanf( fp, "%f", &restrs->lo_bnd ) < 1 )
		goto err;
	    restrs->up_bnd = large_BOUND;   /* set to default; but presence of */
	                                    /* bound is indicated by stat value */
	    restrs->stat = bnd_lower;
	}
	else if ( EQ( key, ultyp ) ) 
	{
	    if ( fscanf( fp, "%f %f", &restrs->up_bnd, &restrs->lo_bnd ) < 2 )
		goto err;
	    restrs->stat = bnd_uplo;
	}
	else if ( EQ( key, uldtyp ) ) 
	{
	    if ( fscanf( fp, "%f %f %f", &restrs->up_bnd, &restrs->lo_bnd, &restrs->dist ) < 3 )
		goto err;
	    restrs->stat = bnd_uld;
	}
	else
	{
	    printf( "Illegal keyword: %s\n", key );
	    goto err;
	}

	done = FlushLine( fp ) < 0;

	if ( OptionTrue( opt_prtrst ) )
	{
	    WriteAtom( stdout, &restrs->atom_A, use_ext );
	    putchar( ' ' );
	    WriteAtom( stdout, &restrs->atom_B, use_ext );
	    printf( " %f %f %f\n", restrs->lo_bnd, restrs->up_bnd, restrs->dist );
	}
	restrs++;
    }
    return( restr_read );

err:
    printf( " * Read error after reading %i restraints\n", restr_read );
    exit( 1 );
    return( -999 ); /* To prevent compiler warning */
}

/**************** WriteRestraintFile  ****************/
/*
**  Write distance restraints to an Aqua restraint file.
*/

void WriteRestraintFile( FILE *fp, DRestraint *restrs, int DR_count, int skip_count, 
                         Card ident, char *DR_typ, char *format )
{
    char     *fct_name = "WriteRestraintFile";
    char     key[size_KEY], *what;
    char     *upp = "UPP", *low = "LOW", *uplo = "UPLO", *uld = "ULD";
    size_t   l;

    strcpy( key, DR_typ );
    ToUpperStr( key );
    fprintf( fp, "%s\n", ident );
    fprintf( fp, "count %i type %s\n", DR_count - skip_count, format );
    while ( DR_count-- )
    {
	if ( !restrs->keep ) 
	{
	    restrs++;
	    continue;
	}
	switch( restrs->stat )
	{
	    case bnd_upper:
		    what = upp;
		    break;
	    case bnd_lower:
		    what = low;
		    break;
	    case bnd_uplo:
		    what = uplo;
		    break;
	    case bnd_uld:
		    what = uld;
		    break;
	    default:
		    printf( "* unexpected restraint status <%i>\n", restrs->stat );
		    exit( 1 );
	}
	fprintf( fp, "%3s%-4s ", key, what );
	if ( ( l = LenChars( restrs->atom_A.chain, size_ID ) ) )
	{
	    fprintf( fp, "CHAIN " );
	    FPrintChars( fp, restrs->atom_A.chain, l );
	    fputc( ' ', fp );
	}
	FPrintChars( fp, restrs->atom_A.res_nam, 4 );
	fprintf( fp, " %3i ", restrs->atom_A.res_num_orig );
	if ( restrs->atom_A.res_insert != ' ' )
	    fprintf( fp, "INSERT %c ", restrs->atom_A.res_insert );
	FPrintChars( fp, restrs->atom_A.atom_ext, 5 );
	fputc( ' ', fp );
	if ( ( l = LenChars( restrs->atom_B.chain, size_ID ) ) )
	{
	    fprintf( fp, "CHAIN " );
	    FPrintChars( fp, restrs->atom_B.chain, l );
	    fputc( ' ', fp );
	}
	FPrintChars( fp, restrs->atom_B.res_nam, 4 );
	fprintf( fp, " %3i ", restrs->atom_B.res_num_orig );
	if ( restrs->atom_B.res_insert != ' ' )
	    fprintf( fp, "INSERT %c ", restrs->atom_B.res_insert );
	FPrintChars( fp, restrs->atom_B.atom_ext, 5 );
	switch( restrs->stat )
	{
	    case bnd_upper:
		    fprintf( fp, " %10.3f\n", restrs->up_bnd );
		    break;
	    case bnd_lower:
		    fprintf( fp, " %10.3f\n", restrs->lo_bnd );
		    break;
	    case bnd_uplo:
		    fprintf( fp, " %10.3f %10.3f\n", restrs->up_bnd, restrs->lo_bnd );
		    break;
	    case bnd_uld:
		    fprintf( fp, " %10.3f %10.3f %10.3f\n", restrs->up_bnd, restrs->lo_bnd, restrs->dist );
		    break;
        default:
            FATAL("Impossible code error");
	}
	restrs++;
    }
}

#undef size_KEY

/****************  ReadAtomLIB  ****************/
/*
**  Process the atom name definitions from Aqua's AtomLIB.
**
**  Format assumed:
**  (1) standard Aqua header
**  (2) standard Aqua header
**  (3) $ ATOM_COUNT: <atom_count>
**  (4) $ BEGIN_DATA:
**  (5) def <res_name> <res_num> <atom_name_external> <atom_name_internal> 
**  (repeat 5)
**  or:
**  (5) map <res_name> <res_num> <chain_id> <atom_name_external> <atom_name_internal>
**  (repeat 5)
**
**  A residue number or chain id may be given as the wildcard symbol '*'.
**  These will match all residues and/or chains (see ExpandAtoms/FindAtom3).
**  Lines starting with # are treated as comments.
**  Empty or blank lines force end of reading.
**  Other lines generate an error.
**
**  NOTES:
**   - res_num is treated as res_num_orig
**   - chain_id of '.' is converted to ' '
**   - for lines beginning with "def" the chain_id is set to '*'
**
*/

Atom *ReadAtomLIB( FILE *fp, int *at_count )
{
    char     *fct_name = "ReadAtomLIB";
    Atom     *atoms, *list;
    int      num_at = 0, i;
    Boolean  done = false, foundmap = false, founddef = false;
    Card     txt[5];

    if ( !ReadAquaIDLines( fp ) )
	return( NULL );
        
/* process file header */

    *at_count = 0;
    while ( !done )
    {
	if ( ! ProcessAquaLine( line, descr, inp, size_LINE, fp ) )
	{
	    if ( !iaf )
		exit( 1 );
	    return( NULL );
	}
        if      ( EQn( descr, "ATOM_COUNT", 10 ) )
            sscanf( inp, "%i", at_count );
        else if ( EQn( descr, "BEGIN_DATA", 10 ) )
            done = true;
    }

/* allocate the list */

    if ( !at_count ) 
    {
	puts( " * Number of atoms not specified in AtomLIB header" );
	return( NULL );
    }
    atoms = CREATE_NEW( Atom, *at_count );
    if ( !atoms )
	NOMEM( "atoms" );
    InitAtomS( atoms, *at_count );
    list = atoms;
    
/* read the list */

    done = false;
    while ( !done )
    {
	if ( !ReadWord( line, size_LINE, true, fp ) )     /* input word; eol test */
	    break;
	if ( EQn( line, "#", 1 ) )
	{
	    done = FlushLine( fp ) < 0;
	    continue;
	}
        else if ( EQn( "def", line, 3 ) )                 /* start of atom def */
        {
	    if ( foundmap )
	    {
		ERROR( "skipping 'def' - expect 'map' only" );
		done = FlushLine( fp ) < 0;
		continue;
	    }
	    founddef = true;
            if ( ++num_at > *at_count )
                FATAL( "Number of atom definitions exceeds count in file header" );
	                                                  /* store atom def */
	    for ( i=0; i<4; i++ )                         /* input words; eol test */
		if ( !ReadWord( txt[i], size_LINE, true, fp ) )
		    goto err;
	    CopyChars( atoms->res_nam, txt[0], size_ID );
	    atoms->chain[0] = '*';
	    CopyChars( atoms->atom_ext, txt[2], size_ID );
	    CopyChars( atoms->atom_int, txt[3], size_ID );
	    if ( EQ( "*", txt[1] ) )
		atoms->res_num_orig = wild_RESNUM;
	    else
	    {
		if ( sscanf( txt[1], "%i", &atoms->res_num_orig ) < 1 )
		    goto err;
	    }

            atoms++;
	    done = FlushLine( fp ) < 0;
        }
        else if ( EQn( "map", line, 3 ) )                 /* start of atom def (new style) */
        {
	    if ( founddef )
	    {
		ERROR( "skipping 'map' - expect 'def' only" );
		done = FlushLine( fp ) < 0;
		continue;
	    }
	    foundmap = true;
            if ( ++num_at > *at_count )
                FATAL( "Number of atom definitions exceeds count in file header" );
	                                                  /* store atom def */
	    for ( i=0; i<5; i++ )                         /* input words; eol test */
		if ( !ReadWord( txt[i], size_LINE, true, fp ) )
		    goto err;
	    CopyChars( atoms->res_nam, txt[0], size_ID );
	    if ( NOTEQ( txt[2], "." ) )
		CopyChars( atoms->chain, txt[2], size_ID );
	    CopyChars( atoms->atom_ext, txt[3], size_ID );
	    CopyChars( atoms->atom_int, txt[4], size_ID );
	    if ( EQ( "*", txt[1] ) )
		atoms->res_num_orig = wild_RESNUM;
	    else
	    {
		if ( sscanf( txt[1], "%i", &atoms->res_num_orig ) < 1 )
		    goto err;
	    }

            atoms++;
	    done = FlushLine( fp ) < 0;
        }
	else
	{
	    puts( "Unexpected line:" );
	    puts( line );
	    goto err;
	}
    }

    if ( num_at != *at_count )
    {
	printf( "Number of atom definitions read (%i)\n", num_at );
	printf( "differs from number defined in header (%i)\n", *at_count );
    }
    *at_count = num_at;
    return( list );

err:
    printf( " * Error while reading atom definition # %i\n", num_at );
    return( NULL );
}

/****************  ReadPseudoLIB  ****************/
/*
**  Process the pseudo atom definitions from Aqua's PseudoLIB.
**
**  Format assumed:
**  (1) standard Aqua header
**  (2) standard Aqua header
**  (3) $ PSEUDO_COUNT: <pseudo_count>
**  (4) $ BEGIN_DATA:
**  (5) def <res_name> <res_num> <pseudo_nam> <pseudo_typ> <form_nam[1]> etc.
**  (repeat 5)
**
**  A residue number may be given as the wildcard symbol '*'.
**  Lines starting with # are treated as comments.
**  Empty or blank lines force end of reading.
**  Other lines generate an error.
**
**  NOTE: res_num is treated as res_num_orig
**        chain id not yet read
**  Handling in ExpandPsatoms such that for ** wildcarded residues **
**  occurrences in every chain are handled.
**
*/

Psatom *ReadPseudoLIB( FILE *fp, int *pseudo_count )
{
    char     *fct_name = "ReadPseudoLIB";
    Psatom   *psatoms, *pslist;
    int      num_psat = 0, i;
    Boolean  done = false;
    Card     txt[4];

    if ( !ReadAquaIDLines( fp ) )
	return( NULL );
        
/* process file header */

    *pseudo_count = 0;
    while ( !done )
    {
	if ( ! ProcessAquaLine( line, descr, inp, size_LINE, fp ) )
	{
	    if ( !iaf )
		exit( 1 );
	    return( NULL );
	}
        if      ( EQn( descr, "PSEUDO_COUNT", 12 ) )
            sscanf( inp, "%i", pseudo_count );
        else if ( EQn( descr, "BEGIN_DATA", 10 ) )
            done = true;
    }

/* allocate the list */

    if ( !pseudo_count ) 
    {
	puts( " * Number of pseudo atoms not specified in PseudoLIB header" );
	return( NULL );
    }
    psatoms = CREATE_NEW( Psatom, *pseudo_count );
    if ( !psatoms )
	NOMEM( "psatoms" );
    InitPsatomS( psatoms, *pseudo_count );
    pslist = psatoms;
    
/* read the list */

    done = false;
    while ( !done )
    {
	if ( !ReadWord( line, size_LINE, true, fp ) )     /* input word; eol test */
	    break;
	if ( EQn( line, "#", 1 ) )
	{
	    done = FlushLine( fp ) < 0;
	    continue;
	}
        else if ( EQn( "def", line, 3 ) )                 /* start of pseudo */
        {
            if ( ++num_psat > *pseudo_count )
            {
                puts( " * Number of pseudo atom definitions exceeds count in file header" );
                exit( 1 );
            }
	                                                  /* store pseudo */
	    for ( i=0; i<4; i++ )                         /* input words; eol test */
		if ( !ReadWord( txt[i], size_LINE, true, fp ) )
		    goto err;
	    CopyChars( psatoms->res_nam, txt[0], size_ID );
	    CopyChars( psatoms->pseudo_nam, txt[2], size_ID );
	    if ( EQ( "*", txt[1] ) )
		psatoms->res_num_orig = wild_RESNUM;
	    else
	    {
		if ( sscanf( txt[1], "%i", &psatoms->res_num_orig ) < 1 )
		    goto err;
	    }
	    if ( sscanf( txt[3], "%i", &psatoms->pseudo_typ ) < 1 )
		goto err;

		                                          /* store constituent atoms */
	    i = 0;
            while ( ReadWord( line, size_LINE, true, fp ) )  /* input word; eol test */
            {
		if ( ( i = (psatoms->num_form)++ ) == max_PSFORM )
		{
		    printf( " * Out of array bound, change max_PSFORM\n" );
		    exit( 1 );
		}
                CopyChars( psatoms->form_nam[i], line, size_ID);
            }
            psatoms++;
	    done = FlushLine( fp ) < 0;
        }
	else
	{
	    puts( "Unexpected line:" );
	    puts( line );
	    goto err;
	}
    }

    if ( num_psat != *pseudo_count )
    {
	printf( "Number of pseudo atom definitions read (%i)\n", num_psat );
	printf( "differs from number defined in header (%i)\n", *pseudo_count );
    }
    *pseudo_count = num_psat;
    return( pslist );

err:
    printf( " * Error while reading pseudo atom definition # %i\n", num_psat );
    return( NULL );
}

